package com.pan.template.impl;

import com.pan.entity.UMLInformation;
import com.pan.template.ParseTemplate;
import com.pan.template.ctrl.CtrlCellY;
import com.pan.template.ctrl.CtrlClassXY;
import com.pan.utils.TemplateReader;
import com.pan.utils.UmlConfig;

import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.StringJoiner;
import java.util.function.BiFunction;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class XmlParseTemplate implements ParseTemplate {


    public XmlParseTemplate() {

    }


    private TemplateReader templateReader = TemplateReader.INSTANCE;

    private Pattern paramPattern = Pattern.compile("#\\{.*?\\}");
    private Pattern signPattern = Pattern.compile("(?<=\\{).*?(?=(:|\\}))");
    private Pattern defaultPattern = Pattern.compile("(?<=:).*(?=\\})");


    private String parseTemplate(String template, Map<String, String> paramMap) {
        Matcher matcher = paramPattern.matcher(template);
//        jdk8
        StringBuffer result = new StringBuffer();
        for (; matcher.find(); ) {
            String group = matcher.group();
            Matcher signMatcher = signPattern.matcher(group);
            signMatcher.find();
            String sign = signMatcher.group();
            if (paramMap.containsKey(sign)) {
//                System.out.println(group);
//                System.out.println(paramMap.get(sign));
                matcher.appendReplacement(result, paramMap.get(sign).replaceAll("\\$", "#"));
            } else {
                Matcher defaultValueMatcher = defaultPattern.matcher(group);
                if (defaultValueMatcher.find()) {
                    matcher.appendReplacement(result, defaultValueMatcher.group().trim());
                } else {
                    throw new RuntimeException("缺乏该参数值 The value of this parameter is missing: " + sign);
                }
            }
        }
        matcher.appendTail(result);
        return result.toString();

//        jdk11
//        String result = matcher.replaceAll(matchResult -> {
//            String group = matchResult.group();
//            Matcher signMatcher = signPattern.matcher(group);
//            signMatcher.find();
//            String sign = signMatcher.group();
//            if (paramMap.containsKey(sign)){
//                return paramMap.get(sign);
//            }
//            Matcher defaultValueMatcher = defaultPattern.matcher(group);
//            if (defaultValueMatcher.find()){
//                return defaultValueMatcher.group().strip();
//            }else {
//                throw new RuntimeException("缺乏该参数值 The value of this parameter is missing: "+sign);
//            }
//        });
//        return result;
    }

    private String getCell(String value, String parentId, int y, int width) {
        return getCell(value, parentId, String.valueOf(y), String.valueOf(width), UmlConfig.INSTANCE.getHeightUnit() + "");
    }

    private String getCell(String value, String parentId, String y, String width, String height) {
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("value", value);
        String id = "";
        if (parentId != null) {
            paramMap.put("parentId", parentId);
            id += parentId;
        }
        id += value.replaceAll("\\s", "_");
        paramMap.put("id", id);
        paramMap.put("y", y + "");
        if (width != null) {
            paramMap.put("width", width + "");
        }
        if (height != null) {
            paramMap.put("height", "" + height);
        }
        String template = templateReader.readCell();
        String result = parseTemplate(template, paramMap);
        return result;
    }


    public String getLine(String parentId, int y, int width) {
        return getLine(parentId, y, width, null);
    }

    public String getLine(String parentId, int y, int width, Integer height) {
        String template = templateReader.readLine();
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("parentId", parentId);
        paramMap.put("id", parentId + "_line");
        paramMap.put("y", y + "");
        paramMap.put("width", width + "");
        if (height != null) {
            paramMap.put("height", height.toString());
        }
        String result = parseTemplate(template, paramMap);
        return result;
    }


    public String getClassName(String id, String value, String x, String y,
                               String width, String height) {
        Map<String, String> map = new HashMap<>();
        map.put("id", id);
        map.put("value", value);
        map.put("x", x);
        map.put("y", y);
        map.put("width", width);
        map.put("height", height);
        String template = templateReader.readClassName();
        String result = parseTemplate(template, map);
        return result;

    }

    private CtrlClassXY ctrlXY;

    {
        ctrlXY = CtrlClassXY.INSTANCE;
    }

    private CtrlCellY ctrlY;

    {
        ctrlY = CtrlCellY.INSTANCE;
    }


    public String toContent(UMLInformation umlInformation) {
        ctrlY.reset();
        String classId = umlInformation.getClassName();
        int classWidth = UmlConfig.INSTANCE.getWidth(umlInformation.getXSize());
        int classHeight = UmlConfig.INSTANCE.getHeight(umlInformation.getYSize());
        int[] xy = ctrlXY.nextXY(classWidth, classHeight);
        StringJoiner result = new StringJoiner("\n");
        result.add(getClassName(classId, classId, xy[0] + "", xy[1] + "", classWidth + "", classHeight + ""));
        ctrlY.nextCellY();
        for (String fieldStr : umlInformation.getFields()) {
            result.add(getCell(fieldStr, classId, ctrlY.nextCellY(), classWidth));
        }
        result.add(getLine(classId, ctrlY.nextLineY(), classWidth));
        for (String constructorStr : umlInformation.getConstructors()) {
            result.add(getCell(constructorStr, classId, ctrlY.nextCellY(), classWidth));
        }
        for (String methodStr : umlInformation.getMethods()) {
            result.add(getCell(methodStr, classId, ctrlY.nextCellY(), classWidth));
        }
        return result.toString();
    }


}
